<!-- 
  watchEffect 语法：


      const stop = watchEffect(effect)

          0. effect 是一个函数，该函数默认会执行一次，且会收集该函数内使用到的响应式数据作为依赖，
              当这些依赖项发生变化时，该 effect 会再次执行。
              PS: 依赖项的收集是同步收集

              function (onClean) {}

          1. stop  是停止该 watch 的一个函数
          2. onClean 是清理回调函数，它接受一个函数，该函数的执行时机是在下一次 effect 要被执行前执行

  watch 语法：

      watch(来源, effect, 可选配置对象)

          1. effect 不会默认触发一次，而是需要在依赖改变之后才会触发。
          2. 可选配置对象: 可以配置 immediate 、 deep 、 flush
          3. 来源:
              1. 一个 ref 数据（不要 .value）
              2. 一个 reactive 数据
                  因为默认会开启深度监听，所以不推荐。
              3. 一个 函数 返回一个值 (getter)
              4. 包含上面三种形式的一个数组

  watch 与 watchEffect 的区别：

      1. 两者都是处理监听的工作，当数据发生变化时去做一些工作。
      2. watch 是懒执行的，watchEffect 会立即执行一次
      3. watch 的依赖更明确，watchEffect 是在第一次执行该 effect 回调时同步收集的


 -->

<template>
  <div>
    <h1>App</h1>
    <p>original: {{ original }}</p>
    <p>copy: {{ copy }}</p>
    <button @click="original.count++">修改 original</button>
    <button @click="copy.count++">修改 copy</button>
    <button @click="stop">STOP</button>

    <button @click="open = !open">Open</button>

    <input type="text" v-model="keyword" />

    <ul>
      <li v-for="item in list" :key="item.id">{{ item.name }}</li>
    </ul>

    <hr />

    <button @click="count++">count: {{ count }}</button>
    <button>plusCount: {{ plusCount }}</button>

    <button @click="person.name += '~'">person.name: {{ person.name }}</button>
    <button @click="person.age++">person.age: {{ person.age }}</button>
    <button @click="person.address.city = '上海市'">
      person.address.city: {{ person.address.city }}
    </button>
  </div>
</template>

<script>
import { computed, reactive, readonly, ref, watch, watchEffect } from 'vue'

export default {
  setup() {
    const count = ref(1)

    const plusCount = computed(() => count.value * 2)

    const person = reactive({
      name: '张三',
      age: 18,
      address: {
        city: '深圳市'
      }
    })

    // 监听一个 ref 数据
    // watch(count, (newVal) => {
    //   console.log('watch count', newVal)
    // })

    // // 监听一个 computed 数据
    // watch(plusCount, (newVal) => {
    //   console.log('watch plusCount', newVal)
    // })

    // 监听一个 reactive 数据
    // watch(person, (newVal) => {
    //   console.log('watch person', newVal)
    // })

    // 监听一个 reactive 数据的属性
    // watch(
    //   () => person.name,
    //   (newVal) => {
    //     console.log('watch person.name', newVal)
    //   }
    // )

    // 写成一个数组形式
    watch([count, () => person.name], (newVal) => {
      console.log('watch []', newVal)
    })

    const original = reactive({ count: 0 })

    const copy = readonly(original)

    const stop = watchEffect((onClean) => {
      // 用来做响应性追踪
      console.log('effect', copy.count)

      onClean(() => {
        console.log('clean')
      })
    })

    const open = ref(true)

    const keyword = ref('')
    const list = ref([])

    watchEffect((onClean) => {
      let url = `http://localhost:3000/books`
      url += `?name_like=${keyword.value}`
      const timer = setTimeout(() => {
        fetch(url)
          .then((response) => response.json())
          .then((result) => {
            list.value = result
          })
      }, 500)

      onClean(() => {
        clearTimeout(timer)
      })
    })

    // watchEffect((onClean) => {
    //   console.log('effect', open.value)
    //   const timer = setInterval(() => {
    //     console.log('interval')
    //   }, 1000)

    //   onClean(() => {
    //     clearInterval(timer)
    //   })
    // })

    watchEffect((onClean) => {
      console.log(open.value)

      const handler = () => {
        console.log('click')
      }

      window.addEventListener('click', handler)

      onClean(() => {
        window.removeEventListener('click', handler)
      })
    })

    // // 更改源属性会触发其依赖的侦听器
    // original.count++

    // // 更改该只读副本将会失败，并会得到一个警告
    // copy.count++ // warning

    return {
      person,
      plusCount,
      count,
      open,
      original,
      copy,
      stop,
      list,
      keyword
    }
  }
}
</script>
